/* proto.c
 * Routines for protocol tree
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */


static void mark_truncated(char *label_str, size_t name_pos, const size_t size)
{
    static const char  trunc_str[] = " [" UTF8_HORIZONTAL_ELLIPSIS "]";
    const size_t       trunc_len = sizeof(trunc_str)-1;
    char              *last_char;

    /* ..... field_name: dataaaaaaaaaaaaa
     *                 |
     *                 ^^^^^ name_pos
     *
     * ..... field_name […]: dataaaaaaaaaaaaa
     *
     * name_pos==0 means that we have only data or only a field_name
     */

    if (name_pos < size - trunc_len) {
        memmove(label_str + name_pos + trunc_len, label_str + name_pos, size - name_pos - trunc_len);
        memcpy(label_str + name_pos, trunc_str, trunc_len);

        /* in general, label_str is UTF-8
           we can truncate it only at the beginning of a new character
           we go backwards from the byte right after our buffer and
            find the next starting byte of a UTF-8 character, this is
            where we cut
           there's no need to use g_utf8_find_prev_char(), the search
            will always succeed since we copied trunc_str into the
            buffer */
        /* g_utf8_prev_char does not deference the memory address
         * passed in (until after decrementing it, so it is perfectly
         * legal to pass in a pointer one past the last element.
         */
        last_char = g_utf8_prev_char(label_str + size);
        *last_char = '\0';

    } else if (name_pos < size)
        (void) g_strlcpy(label_str + name_pos, trunc_str, size - name_pos);
}

static void label_mark_truncated(char *label_str, size_t name_pos)
{
    mark_truncated(label_str, name_pos, ITEM_LABEL_LENGTH);
}

static size_t label_fill(char *label_str, size_t pos, const hf_info *hfinfo, const char *text)
{
    size_t name_pos;

    /* "%s: %s", hfinfo->name, text */
    name_pos = pos = label_concat(label_str, pos, hfinfo->name);
    if (!(hfinfo->display & BASE_NO_DISPLAY_VALUE)) {
        pos = label_concat(label_str, pos, ": ");
        pos = label_strcpy(label_str, ITEM_LABEL_LENGTH, pos, text ? text : "(null)", label_strcat_flags(hfinfo));
    }

    if (pos >= ITEM_LABEL_LENGTH) {
        /* Uh oh, we don't have enough room. Tell the user that the field is truncated. */
        label_mark_truncated(label_str, name_pos);
    }

    return pos;
}

static size_t label_fill_descr(char *label_str, size_t pos, const hf_info *hfinfo, const char *text, const char *descr)
{
    size_t name_pos;

    /* "%s: %s (%s)", hfinfo->name, text, descr */
    name_pos = pos = label_concat(label_str, pos, hfinfo->name);
    if (!(hfinfo->display & BASE_NO_DISPLAY_VALUE)) {
        pos = label_concat(label_str, pos, ": ");
        if (hfinfo->display & BASE_UNIT_STRING) {
            pos = label_concat(label_str, pos, descr ? descr : "(null)");
            pos = label_concat(label_str, pos, text ? text : "(null)");
        } else {
            pos = label_concat(label_str, pos, text ? text : "(null)");
            pos = label_concat(label_str, pos, " (");
            pos = label_concat(label_str, pos, descr ? descr : "(null)");
            pos = label_concat(label_str, pos, ")");
        }
    }

    if (pos >= ITEM_LABEL_LENGTH) {
        /* Uh oh, we don't have enough room. Tell the user that the field is truncated. */
        label_mark_truncated(label_str, name_pos);
    }

    return pos;
}

static const char *hf_try_val_to_str(uint32_t value, const hf_info *hfinfo)
{
    if (hfinfo->display & BASE_RANGE_STRING)
        return try_rval_to_str(value, (const range_string *) hfinfo->strings);

    if (hfinfo->display & BASE_EXT_STRING) {
        if (hfinfo->display & BASE_VAL64_STRING)
            return try_val64_to_str_ext(value, (val64_string_ext *) hfinfo->strings);
        else
            return try_val_to_str_ext(value, (value_string_ext *) hfinfo->strings);
    }

    if (hfinfo->display & BASE_VAL64_STRING)
        return try_val64_to_str(value, (const val64_string *) hfinfo->strings);

    if (hfinfo->display & BASE_UNIT_STRING)
        return unit_name_string_get_value(value, (const struct unit_name_string*) hfinfo->strings);

    return try_val_to_str(value, (const value_string *) hfinfo->strings);
}

static const char *hf_try_val64_to_str(uint64_t value, const hf_info *hfinfo)
{
    if (hfinfo->display & BASE_VAL64_STRING) {
        if (hfinfo->display & BASE_EXT_STRING)
            return try_val64_to_str_ext(value, (val64_string_ext *) hfinfo->strings);
        else
            return try_val64_to_str(value, (const val64_string *) hfinfo->strings);
    }

    if (hfinfo->display & BASE_RANGE_STRING)
        return try_rval64_to_str(value, (const range_string *) hfinfo->strings);

    if (hfinfo->display & BASE_UNIT_STRING)
        return unit_name_string_get_value64(value, (const struct unit_name_string*) hfinfo->strings);

    /* If this is reached somebody registered a 64-bit field with a 32-bit
     * value-string, which isn't right. */
    REPORT_DISSECTOR_BUG("field %s is a 64-bit field with a 32-bit value_string",
        hfinfo->abbrev);

    /* This is necessary to squelch MSVC errors; is there
       any way to tell it that DISSECTOR_ASSERT_NOT_REACHED()
       never returns? */
    return NULL;
}

static const char *hf_try_double_val_to_str(double value, const hf_info *hfinfo)
{
    if (hfinfo->display & BASE_UNIT_STRING)
        return unit_name_string_get_double(value, (const struct unit_name_string*)hfinfo->strings);

    REPORT_DISSECTOR_BUG("field %s (FT_DOUBLE) has no base_unit_string", hfinfo->abbrev);

    /* This is necessary to squelch MSVC errors; is there
       any way to tell it that DISSECTOR_ASSERT_NOT_REACHED()
       never returns? */
    return NULL;
}

static const char *hf_try_val_to_str_const(uint32_t value, const hf_info *hfinfo, const char *unknown_str)
{
    const char *str = hf_try_val_to_str(value, hfinfo);

    return (str) ? str : unknown_str;
}

static const char *hf_try_val64_to_str_const(uint64_t value, const hf_info *hfinfo, const char *unknown_str)
{
    const char *str = hf_try_val64_to_str(value, hfinfo);

    return (str) ? str : unknown_str;
}

int hfinfo_bitshift(const hf_info *hfinfo)
{
    return ws_ctz(hfinfo->bitmask);
}

static int hfinfo_bitoffset(const hf_info *hfinfo)
{
    if (!hfinfo->bitmask) {
        return 0;
    }

    /* ilog2 = first set bit, counting 0 as the last bit; we want 0
     * as the first bit */
    return hfinfo_container_bitwidth(hfinfo) - 1 - ws_ilog2(hfinfo->bitmask);
}

static int hfinfo_mask_bitwidth(const hf_info *hfinfo)
{
    if (!hfinfo->bitmask) {
        return 0;
    }

    /* ilog2 = first set bit, ctz = last set bit */
    return ws_ilog2(hfinfo->bitmask) - ws_ctz(hfinfo->bitmask) + 1;
}

static int hfinfo_type_bitwidth(enum ftenum type)
{
    int bitwidth = 0;

    switch (type) {
    case FT_CHAR:
    case FT_UINT8:
    case FT_INT8:
        bitwidth = 8;
        break;
    case FT_UINT16:
    case FT_INT16:
        bitwidth = 16;
        break;
    case FT_UINT24:
    case FT_INT24:
        bitwidth = 24;
        break;
    case FT_UINT32:
    case FT_INT32:
        bitwidth = 32;
        break;
    case FT_UINT40:
    case FT_INT40:
        bitwidth = 40;
        break;
    case FT_UINT48:
    case FT_INT48:
        bitwidth = 48;
        break;
    case FT_UINT56:
    case FT_INT56:
        bitwidth = 56;
        break;
    case FT_UINT64:
    case FT_INT64:
        bitwidth = 64;
        break;
    default:
        DISSECTOR_ASSERT_NOT_REACHED();
        break;
    }
    return bitwidth;
}

static int hfinfo_container_bitwidth(const hf_info *hfinfo)
{
    if (!hfinfo->bitmask) {
        return 0;
    }

    if (hfinfo->type == FT_BOOLEAN) {
        return hfinfo->display; /* hacky? :) */
    }

    return hfinfo_type_bitwidth(hfinfo->type);
}

static int hfinfo_hex_digits(const hf_info *hfinfo)
{
    int bitwidth;

    /* If we have a bitmask, hfinfo->type is the width of the container, so not
     * appropriate to determine the number of hex digits for the field.
     * So instead, we compute it from the bitmask.
     */
    if (hfinfo->bitmask != 0) {
        bitwidth = hfinfo_mask_bitwidth(hfinfo);
    } else {
        bitwidth = hfinfo_type_bitwidth(hfinfo->type);
    }

    /* Divide by 4, rounding up, to get number of hex digits. */
    return (bitwidth + 3) / 4;
}

const char *hfinfo_char_value_format_display(int display, char buf[7], uint32_t value)
{
    char *ptr = &buf[6];
    static const char hex_digits[16] =
    { '0', '1', '2', '3', '4', '5', '6', '7',
      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

    *ptr = '\0';
    *(--ptr) = '\'';
    /* Properly format value */
    if (g_ascii_isprint(value)) {
        /*
         * Printable, so just show the character, and, if it needs
         * to be escaped, escape it.
         */
        *(--ptr) = value;
        if (value == '\\' || value == '\'')
            *(--ptr) = '\\';
    } else {
        /*
         * Non-printable; show it as an escape sequence.
         */
        switch (value) {

        case '\0':
            /*
             * Show a NUL with only one digit.
             */
            *(--ptr) = '0';
            break;

        case '\a':
            *(--ptr) = 'a';
            break;

        case '\b':
            *(--ptr) = 'b';
            break;

        case '\f':
            *(--ptr) = 'f';
            break;

        case '\n':
            *(--ptr) = 'n';
            break;

        case '\r':
            *(--ptr) = 'r';
            break;

        case '\t':
            *(--ptr) = 't';
            break;

        case '\v':
            *(--ptr) = 'v';
            break;

        default:
            switch (FIELD_DISPLAY(display)) {

            case BASE_OCT:
                *(--ptr) = (value & 0x7) + '0';
                value >>= 3;
                *(--ptr) = (value & 0x7) + '0';
                value >>= 3;
                *(--ptr) = (value & 0x7) + '0';
                break;

            case BASE_HEX:
                *(--ptr) = hex_digits[value & 0x0F];
                value >>= 4;
                *(--ptr) = hex_digits[value & 0x0F];
                *(--ptr) = 'x';
                break;

            default:
                REPORT_DISSECTOR_BUG("Invalid base: %d", FIELD_DISPLAY(display));
            }
        }
        *(--ptr) = '\\';
    }
    *(--ptr) = '\'';
    return ptr;
}

static const char *hfinfo_number_value_format_display(const hf_info *hfinfo, int display, char buf[NUMBER_LABEL_LENGTH], uint32_t value)
{
    char *ptr = &buf[NUMBER_LABEL_LENGTH-1];
    bool isint = FT_IS_INT(hfinfo->type);

    *ptr = '\0';
    /* Properly format value */
    switch (FIELD_DISPLAY(display)) {
    case BASE_DEC:
        return isint ? int_to_str_back(ptr, (int32_t) value) : uint_to_str_back(ptr, value);

    case BASE_DEC_HEX:
        *(--ptr) = ')';
        ptr = hex_to_str_back_len(ptr, value, hfinfo_hex_digits(hfinfo));
        *(--ptr) = '(';
        *(--ptr) = ' ';
        ptr = isint ? int_to_str_back(ptr, (int32_t) value) : uint_to_str_back(ptr, value);
        return ptr;

    case BASE_OCT:
        return oct_to_str_back(ptr, value);

    case BASE_HEX:
        return hex_to_str_back_len(ptr, value, hfinfo_hex_digits(hfinfo));

    case BASE_HEX_DEC:
        *(--ptr) = ')';
        ptr = isint ? int_to_str_back(ptr, (int32_t) value) : uint_to_str_back(ptr, value);
        *(--ptr) = '(';
        *(--ptr) = ' ';
        ptr = hex_to_str_back_len(ptr, value, hfinfo_hex_digits(hfinfo));
        return ptr;

//        case BASE_PT_UDP:
//        case BASE_PT_TCP:
//        case BASE_PT_DCCP:
//        case BASE_PT_SCTP:
//            port_with_resolution_to_str_buf(buf, NUMBER_LABEL_LENGTH,
//                    display_to_port_type((field_display_e)display), value);
//            return buf;
//        case BASE_OUI:
//            {
//            uint8_t p_oui[3];
//            const char *manuf_name;

//            p_oui[0] = value >> 16 & 0xFF;
//            p_oui[1] = value >> 8 & 0xFF;
//            p_oui[2] = value & 0xFF;

//            /* Attempt an OUI lookup. */
//            manuf_name = uint_get_manuf_name_if_known(value);
//            if (manuf_name == NULL) {
//                /* Could not find an OUI. */
//                snprintf(buf, NUMBER_LABEL_LENGTH, "%02x:%02x:%02x", p_oui[0], p_oui[1], p_oui[2]);
//            }
//            else {
//                /* Found an address string. */
//                snprintf(buf, NUMBER_LABEL_LENGTH, "%02x:%02x:%02x (%s)", p_oui[0], p_oui[1], p_oui[2], manuf_name);
//            }
//            return buf;
//            }

    default:
        REPORT_DISSECTOR_BUG("Invalid base: %d", FIELD_DISPLAY(display));
    }
    return ptr;
}

static const char *hfinfo_number_value_format_display64(const hf_info *hfinfo, int display, char buf[NUMBER_LABEL_LENGTH], uint64_t value)
{
    char *ptr = &buf[NUMBER_LABEL_LENGTH-1];
    bool isint = FT_IS_INT(hfinfo->type);

    *ptr = '\0';
    /* Properly format value */
    switch (FIELD_DISPLAY(display)) {
    case BASE_DEC:
        return isint ? int64_to_str_back(ptr, (int64_t) value) : uint64_to_str_back(ptr, value);

    case BASE_DEC_HEX:
        *(--ptr) = ')';
        ptr = hex64_to_str_back_len(ptr, value, hfinfo_hex_digits(hfinfo));
        *(--ptr) = '(';
        *(--ptr) = ' ';
        ptr = isint ? int64_to_str_back(ptr, (int64_t) value) : uint64_to_str_back(ptr, value);
        return ptr;

    case BASE_OCT:
        return oct64_to_str_back(ptr, value);

    case BASE_HEX:
        return hex64_to_str_back_len(ptr, value, hfinfo_hex_digits(hfinfo));

    case BASE_HEX_DEC:
        *(--ptr) = ')';
        ptr = isint ? int64_to_str_back(ptr, (int64_t) value) : uint64_to_str_back(ptr, value);
        *(--ptr) = '(';
        *(--ptr) = ' ';
        ptr = hex64_to_str_back_len(ptr, value, hfinfo_hex_digits(hfinfo));
        return ptr;

    default:
        REPORT_DISSECTOR_BUG("Invalid base: %d", FIELD_DISPLAY(display));
    }

    return ptr;
}

static const char *hfinfo_number_value_format(const hf_info *hfinfo, char buf[NUMBER_LABEL_LENGTH], uint32_t value)
{
    int display = hfinfo->display;

    if (hfinfo->type == FT_FRAMENUM) {
        /*
         * Frame numbers are always displayed in decimal.
         */
        display = BASE_DEC;
    }

    return hfinfo_number_value_format_display(hfinfo, display, buf, value);
}

static const char *hfinfo_number_value_format64(const hf_info *hfinfo, char buf[NUMBER_LABEL_LENGTH], uint64_t value)
{
    int display = hfinfo->display;

    if (hfinfo->type == FT_FRAMENUM) {
        /*
         * Frame numbers are always displayed in decimal.
         */
        display = BASE_DEC;
    }

    return hfinfo_number_value_format_display64(hfinfo, display, buf, value);
}

static const char *hfinfo_char_value_format(const hf_info *hfinfo, char buf[32], uint32_t value)
{
    /* Get the underlying BASE_ value */
    int display = FIELD_DISPLAY(hfinfo->display);

    return hfinfo_char_value_format_display(display, buf, value);
}

static const char *hfinfo_numeric_value_format(const hf_info *hfinfo, char buf[NUMBER_LABEL_LENGTH], uint32_t value)
{
    /* Get the underlying BASE_ value */
    int display = FIELD_DISPLAY(hfinfo->display);

    if (hfinfo->type == FT_FRAMENUM) {
        /*
         * Frame numbers are always displayed in decimal.
         */
        display = BASE_DEC;
    }

//    if (IS_BASE_PORT(display)) {
//        display = BASE_DEC;
//    } else if (display == BASE_OUI) {
//        display = BASE_HEX;
//    }

    switch (display) {
    case BASE_NONE:
    /* case BASE_DEC: */
    case BASE_DEC_HEX:
    case BASE_OCT: /* XXX, why we're changing BASE_OCT to BASE_DEC? */
    case BASE_CUSTOM:
        display = BASE_DEC;
        break;

    /* case BASE_HEX: */
    case BASE_HEX_DEC:
        display = BASE_HEX;
        break;
    }

    return hfinfo_number_value_format_display(hfinfo, display, buf, value);
}

static const char *hfinfo_numeric_value_format64(const hf_info *hfinfo, char buf[NUMBER_LABEL_LENGTH], uint64_t value)
{
    /* Get the underlying BASE_ value */
    int display = FIELD_DISPLAY(hfinfo->display);

    if (hfinfo->type == FT_FRAMENUM) {
        /*
         * Frame numbers are always displayed in decimal.
         */
        display = BASE_DEC;
    }

    switch (display) {
    case BASE_NONE:
    /* case BASE_DEC: */
    case BASE_DEC_HEX:
    case BASE_OCT: /* XXX, why we're changing BASE_OCT to BASE_DEC? */
    case BASE_CUSTOM:
        display = BASE_DEC;
        break;

    /* case BASE_HEX: */
    case BASE_HEX_DEC:
        display = BASE_HEX;
        break;
    }

    return hfinfo_number_value_format_display64(hfinfo, display, buf, value);
}

static const char *hfinfo_char_vals_format(const hf_info *hfinfo, char buf[32], uint32_t value)
{
    /* Get the underlying BASE_ value */
    int display = FIELD_DISPLAY(hfinfo->display);

    return hfinfo_char_value_format_display(display, buf, value);
}

static const char *hfinfo_number_vals_format(const hf_info *hfinfo, char buf[NUMBER_LABEL_LENGTH], uint32_t value)
{
    /* Get the underlying BASE_ value */
    int display = FIELD_DISPLAY(hfinfo->display);

    if (display == BASE_NONE)
        return NULL;

    if (display == BASE_DEC_HEX)
        display = BASE_DEC;
    if (display == BASE_HEX_DEC)
        display = BASE_HEX;

    return hfinfo_number_value_format_display(hfinfo, display, buf, value);
}

static const char *hfinfo_number_vals_format64(const hf_info *hfinfo, char buf[NUMBER_LABEL_LENGTH], uint64_t value)
{
    /* Get the underlying BASE_ value */
    int display = FIELD_DISPLAY(hfinfo->display);

    if (display == BASE_NONE)
        return NULL;

    if (display == BASE_DEC_HEX)
        display = BASE_DEC;
    if (display == BASE_HEX_DEC)
        display = BASE_HEX;

    return hfinfo_number_value_format_display64(hfinfo, display, buf, value);
}

/* Fills data for bitfield chars with val_strings */
static void fill_label_bitfield_char(const field_info *fi, char *label_str)
{
    char       *p;
    int         bitfield_byte_length, bitwidth;
    uint32_t    unshifted_value;
    uint32_t    value;

    char        buf[32];
    const char *out;

    const hf_info *hfinfo = fi->hfinfo;

    /* Figure out the bit width */
    bitwidth = hfinfo_container_bitwidth(hfinfo);

    /* Un-shift bits */
    value = fvalue_get_uinteger(fi->value);

    unshifted_value = value;
    if (hfinfo->bitmask) {
        unshifted_value <<= hfinfo_bitshift(hfinfo);
    }

    /* Create the bitfield first */
    p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
    bitfield_byte_length = (int) (p - label_str);

    /* Fill in the textual info using stored (shifted) value */
    if (hfinfo->display == BASE_CUSTOM) {
        char tmp[ITEM_LABEL_LENGTH];
        const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hfinfo->strings;

        DISSECTOR_ASSERT(fmtfunc);
        fmtfunc(tmp, value);
        label_fill(label_str, bitfield_byte_length, hfinfo, tmp);
    }
    else if (hfinfo->strings) {
        const char *val_str = hf_try_val_to_str_const(value, hfinfo, "Unknown");

        out = hfinfo_char_vals_format(hfinfo, buf, value);
        if (out == NULL) /* BASE_NONE so don't put integer in descr */
            label_fill(label_str, bitfield_byte_length, hfinfo, val_str);
        else
            label_fill_descr(label_str, bitfield_byte_length, hfinfo, val_str, out);
    }
    else {
        out = hfinfo_char_value_format(hfinfo, buf, value);

        label_fill(label_str, bitfield_byte_length, hfinfo, out);
    }
}

/* Fills data for bitfield ints with val_strings */
static void fill_label_bitfield(const field_info *fi, char *label_str, bool is_signed)
{
    char       *p;
    int         bitfield_byte_length, bitwidth;
    uint32_t    value, unshifted_value;
    char        buf[NUMBER_LABEL_LENGTH];
    const char *out;

    const hf_info *hfinfo = fi->hfinfo;

    /* Figure out the bit width */
    if (fi->flags & FI_VARINT)
        bitwidth = fi->length*8;
    else
        bitwidth = hfinfo_container_bitwidth(hfinfo);

    /* Un-shift bits */
    if (is_signed)
        value = fvalue_get_sinteger(fi->value);
    else
        value = fvalue_get_uinteger(fi->value);

    unshifted_value = value;
    if (hfinfo->bitmask) {
        unshifted_value <<= hfinfo_bitshift(hfinfo);
    }

    /* Create the bitfield first */
    if (fi->flags & FI_VARINT)
        p = decode_bitfield_varint_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
    else
        p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
    bitfield_byte_length = (int) (p - label_str);

    /* Fill in the textual info using stored (shifted) value */
    if (hfinfo->display == BASE_CUSTOM) {
        char tmp[ITEM_LABEL_LENGTH];
        const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hfinfo->strings;

        DISSECTOR_ASSERT(fmtfunc);
        fmtfunc(tmp, value);
        label_fill(label_str, bitfield_byte_length, hfinfo, tmp);
    }
    else if (hfinfo->strings) {
        const char *val_str = hf_try_val_to_str(value, hfinfo);

        out = hfinfo_number_vals_format(hfinfo, buf, value);
        if (hfinfo->display & BASE_SPECIAL_VALS) {
            /*
             * Unique values only display value_string string
             * if there is a match. Otherwise it's just a number
             */
            if (val_str) {
                label_fill_descr(label_str, bitfield_byte_length, hfinfo, val_str, out);
            } else {
                label_fill(label_str, bitfield_byte_length, hfinfo, out);
            }
        } else {
            if (val_str == NULL)
                val_str = "Unknown";

            if (out == NULL) /* BASE_NONE so don't put integer in descr */
                label_fill(label_str, bitfield_byte_length, hfinfo, val_str);
            else
                label_fill_descr(label_str, bitfield_byte_length, hfinfo, val_str, out);
        }
    }
    else {
        out = hfinfo_number_value_format(hfinfo, buf, value);

        label_fill(label_str, bitfield_byte_length, hfinfo, out);
    }
}

static void fill_label_bitfield64(const field_info *fi, char *label_str, bool is_signed)
{
    char       *p;
    int         bitfield_byte_length, bitwidth;
    uint64_t    value, unshifted_value;
    char        buf[NUMBER_LABEL_LENGTH];
    const char *out;

    const hf_info *hfinfo = fi->hfinfo;

    /* Figure out the bit width */
    if (fi->flags & FI_VARINT)
        bitwidth = fi->length*8;
    else
        bitwidth = hfinfo_container_bitwidth(hfinfo);

    /* Un-shift bits */
    if (is_signed)
        value = fvalue_get_sinteger64(fi->value);
    else
        value = fvalue_get_uinteger64(fi->value);

    unshifted_value = value;
    if (hfinfo->bitmask) {
        unshifted_value <<= hfinfo_bitshift(hfinfo);
    }

    /* Create the bitfield first */
    if (fi->flags & FI_VARINT)
        p = decode_bitfield_varint_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
    else
        p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
    bitfield_byte_length = (int) (p - label_str);

    /* Fill in the textual info using stored (shifted) value */
    if (hfinfo->display == BASE_CUSTOM) {
        char tmp[ITEM_LABEL_LENGTH];
        const custom_fmt_func_64_t fmtfunc64 = (const custom_fmt_func_64_t)hfinfo->strings;

        DISSECTOR_ASSERT(fmtfunc64);
        fmtfunc64(tmp, value);
        label_fill(label_str, bitfield_byte_length, hfinfo, tmp);
    }
    else if (hfinfo->strings) {
        const char *val_str = hf_try_val64_to_str(value, hfinfo);

        out = hfinfo_number_vals_format64(hfinfo, buf, value);
        if (hfinfo->display & BASE_SPECIAL_VALS) {
            /*
             * Unique values only display value_string string
             * if there is a match. Otherwise it's just a number
             */
            if (val_str) {
                label_fill_descr(label_str, bitfield_byte_length, hfinfo, val_str, out);
            } else {
                label_fill(label_str, bitfield_byte_length, hfinfo, out);
            }
        } else {
            if (val_str == NULL)
                val_str = "Unknown";

            if (out == NULL) /* BASE_NONE so don't put integer in descr */
                label_fill(label_str, bitfield_byte_length, hfinfo, val_str);
            else
                label_fill_descr(label_str, bitfield_byte_length, hfinfo, val_str, out);
        }
    }
    else {
        out = hfinfo_number_value_format64(hfinfo, buf, value);

        label_fill(label_str, bitfield_byte_length, hfinfo, out);
    }
}

static void fill_label_boolean(const field_info *fi, char *label_str)
{
    char    *p;
    int      bitfield_byte_length = 0, bitwidth;
    uint64_t unshifted_value;
    uint64_t value;

    const hf_info    *hfinfo   = fi->hfinfo;

    value = fvalue_get_uinteger64(fi->value);
    if (hfinfo->bitmask) {
        /* Figure out the bit width */
        bitwidth = hfinfo_container_bitwidth(hfinfo);

        /* Un-shift bits */
        unshifted_value = value;
        unshifted_value <<= hfinfo_bitshift(hfinfo);

        /* Create the bitfield first */
        p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
        bitfield_byte_length = (int) (p - label_str);
    }

    /* Fill in the textual info */
    label_fill(label_str, bitfield_byte_length, hfinfo, tfs_get_string(!!value, hfinfo->strings));
}

static void fill_label_char(const field_info *fi, char *label_str)
{
    const hf_info *hfinfo = fi->hfinfo;
    uint32_t           value;

    char               buf[32];
    const char        *out;

    value = fvalue_get_uinteger(fi->value);

    /* Fill in the textual info */
    if (hfinfo->display == BASE_CUSTOM) {
        char tmp[ITEM_LABEL_LENGTH];
        const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hfinfo->strings;

        DISSECTOR_ASSERT(fmtfunc);
        fmtfunc(tmp, value);
        label_fill(label_str, 0, hfinfo, tmp);
    }
    else if (hfinfo->strings) {
        const char *val_str = hf_try_val_to_str_const(value, hfinfo, "Unknown");

        out = hfinfo_char_vals_format(hfinfo, buf, value);
        label_fill_descr(label_str, 0, hfinfo, val_str, out);
    }
    else {
        out = hfinfo_char_value_format(hfinfo, buf, value);

        label_fill(label_str, 0, hfinfo, out);
    }
}

static void fill_label_number(const field_info *fi, char *label_str, bool is_signed)
{
    const hf_info *hfinfo = fi->hfinfo;
    uint32_t           value;

    char               buf[NUMBER_LABEL_LENGTH];
    const char        *out;

    if (is_signed)
        value = fvalue_get_sinteger(fi->value);
    else
        value = fvalue_get_uinteger(fi->value);

    /* Fill in the textual info */
    if (hfinfo->display == BASE_CUSTOM) {
        char tmp[ITEM_LABEL_LENGTH];
        const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hfinfo->strings;

        DISSECTOR_ASSERT(fmtfunc);
        fmtfunc(tmp, value);
        label_fill(label_str, 0, hfinfo, tmp);
    }
    else if (hfinfo->strings && hfinfo->type != FT_FRAMENUM) {
        /*
         * It makes no sense to have a value-string table for a
         * frame-number field - they're just integers giving
         * the ordinal frame number.
         */
        const char *val_str = hf_try_val_to_str(value, hfinfo);

        out = hfinfo_number_vals_format(hfinfo, buf, value);
        if (hfinfo->display & BASE_SPECIAL_VALS) {
            /*
             * Unique values only display value_string string
             * if there is a match. Otherwise it's just a number
             */
            if (val_str) {
                label_fill_descr(label_str, 0, hfinfo, val_str, out);
            } else {
                label_fill(label_str, 0, hfinfo, out);
            }
        } else {
            if (val_str == NULL)
                val_str = "Unknown";

            if (out == NULL) /* BASE_NONE so don't put integer in descr */
                label_fill(label_str, 0, hfinfo, val_str);
            else
                label_fill_descr(label_str, 0, hfinfo, val_str, out);
        }
    }
//    else if (IS_BASE_PORT(hfinfo->display)) {
//        char tmp[ITEM_LABEL_LENGTH];

//        port_with_resolution_to_str_buf(tmp, sizeof(tmp),
//            display_to_port_type((field_display_e)hfinfo->display), value);
//        label_fill(label_str, 0, hfinfo, tmp);
//    }
    else {
        out = hfinfo_number_value_format(hfinfo, buf, value);

        label_fill(label_str, 0, hfinfo, out);
    }
}

static void fill_label_number64(const field_info *fi, char *label_str, bool is_signed)
{
    const hf_info *hfinfo = fi->hfinfo;
    uint64_t           value;

    char               buf[NUMBER_LABEL_LENGTH];
    const char        *out;

    if (is_signed)
        value = fvalue_get_sinteger64(fi->value);
    else
        value = fvalue_get_uinteger64(fi->value);

    /* Fill in the textual info */
    if (hfinfo->display == BASE_CUSTOM) {
        char tmp[ITEM_LABEL_LENGTH];
        const custom_fmt_func_64_t fmtfunc64 = (const custom_fmt_func_64_t)hfinfo->strings;

        DISSECTOR_ASSERT(fmtfunc64);
        fmtfunc64(tmp, value);
        label_fill(label_str, 0, hfinfo, tmp);
    }
    else if (hfinfo->strings) {
        const char *val_str = hf_try_val64_to_str(value, hfinfo);

        out = hfinfo_number_vals_format64(hfinfo, buf, value);
        if (hfinfo->display & BASE_SPECIAL_VALS) {
            /*
             * Unique values only display value_string string
             * if there is a match. Otherwise it's just a number
             */
            if (val_str) {
                label_fill_descr(label_str, 0, hfinfo, val_str, out);
            } else {
                label_fill(label_str, 0, hfinfo, out);
            }
        } else {
            if (val_str == NULL)
                val_str = "Unknown";

            if (out == NULL) /* BASE_NONE so don't put integer in descr */
                label_fill(label_str, 0, hfinfo, val_str);
            else
                label_fill_descr(label_str, 0, hfinfo, val_str, out);
        }
    }
    else {
        out = hfinfo_number_value_format64(hfinfo, buf, value);

        label_fill(label_str, 0, hfinfo, out);
    }
}

static size_t fill_display_label_float(const field_info *fi, char *label_str)
{
    int display;
    int digits;
    int n;
    double value;

    display = FIELD_DISPLAY(fi->hfinfo->display);
    value = fvalue_get_floating(fi->value);

    if (display == BASE_CUSTOM) {
        const custom_fmt_func_double_t fmtfunc = (const custom_fmt_func_double_t)fi->hfinfo->strings;
        DISSECTOR_ASSERT(fmtfunc);
        fmtfunc(label_str, value);
        return strlen(label_str);
    }

    switch (display) {
    case BASE_NONE:
        if (fi->hfinfo->type == FT_FLOAT)
            digits = FLT_DIG;
        else
            digits = DBL_DIG;

        n = snprintf(label_str, ITEM_LABEL_LENGTH, "%.*g", digits, value);
        break;
    case BASE_DEC:
        n = snprintf(label_str, ITEM_LABEL_LENGTH, "%f", value);
        break;
    case BASE_HEX:
#ifdef __MINGW32__
    double_to_hexfp(label_str, ITEM_LABEL_LENGTH, value);
#else
        n = snprintf(label_str, ITEM_LABEL_LENGTH, "%a", value);
#endif
        break;
    case BASE_EXP:
        n = snprintf(label_str, ITEM_LABEL_LENGTH, "%e", value);
        break;
    default:
        break;
    }
    if (n < 0) {
        return 0; /* error */
    }
    if ((fi->hfinfo->strings) && (fi->hfinfo->display & BASE_UNIT_STRING)) {
        const char *hf_str_val;
        hf_str_val = hf_try_double_val_to_str(value, fi->hfinfo);
        n += protol_strlcpy(label_str + n, hf_str_val, ITEM_LABEL_LENGTH - n);
    }
    if (n > ITEM_LABEL_LENGTH) {
        printf("label length too small");
        return strlen(label_str);
    }

    return n;
}

void fill_label_float(const field_info *fi, char *label_str)
{
    char tmp[ITEM_LABEL_LENGTH];

    fill_display_label_float(fi, tmp);
    label_fill(label_str, 0, fi->hfinfo, tmp);
}

void proto_item_fill_label(const field_info *fi, char *label_str)
{
    const hf_info  *hfinfo;
    const char       *str;
    const uint8_t       *bytes;
    uint32_t            integer;
    uint64_t            integer64;
//    const ipv4_addr_and_mask *ipv4;
//    const ipv6_addr_and_prefix *ipv6;
//    const e_guid_t       *guid;
    char           *name;
//    address            addr;
    char           *addr_str;
    char           *tmp;

    if (!label_str) {
        printf("NULL label_str passed to proto_item_fill_label.");
        return;
    }

    label_str[0]= '\0';

    if (!fi) {
        return;
    }

    hfinfo = fi->hfinfo;

    switch (hfinfo->type) {
    case FT_NONE:
    case FT_PROTOCOL:
        (void) g_strlcpy(label_str, hfinfo->name, ITEM_LABEL_LENGTH);
        break;
    case FT_BOOLEAN:
        fill_label_boolean(fi, label_str);
        break;
    case FT_BYTES:
    case FT_UINT_BYTES:
        tmp = format_bytes_hfinfo(hfinfo,
            fvalue_get_bytes_data(fi->value),
            (unsigned)fvalue_length2(fi->value));
        label_fill(label_str, 0, hfinfo, tmp);
        free(tmp);
        break;
    case FT_CHAR:
        if (hfinfo->bitmask) {
            fill_label_bitfield_char(fi, label_str);
        } else {
            fill_label_char(fi, label_str);
        }
        break;

    /* Four types of integers to take care of:
     *    Bitfield, with val_string
     *    Bitfield, w/o val_string
     *    Non-bitfield, with val_string
     *    Non-bitfield, w/o val_string
     */
    case FT_UINT8:
    case FT_UINT16:
    case FT_UINT24:
    case FT_UINT32:
        if (hfinfo->bitmask) {
            fill_label_bitfield(fi, label_str, false);
        } else {
            fill_label_number(fi, label_str, false);
        }
        break;
    case FT_FRAMENUM:
        fill_label_number(fi, label_str, false);
        break;
    case FT_UINT40:
    case FT_UINT48:
    case FT_UINT56:
    case FT_UINT64:
        if (hfinfo->bitmask) {
            fill_label_bitfield64(fi, label_str, false);
        } else {
            fill_label_number64(fi, label_str, false);
        }
        break;
    case FT_INT8:
    case FT_INT16:
    case FT_INT24:
    case FT_INT32:
        if (hfinfo->bitmask) {
            fill_label_bitfield(fi, label_str, true);
        } else {
            fill_label_number(fi, label_str, true);
        }
        break;
    case FT_INT40:
    case FT_INT48:
    case FT_INT56:
    case FT_INT64:
        if (hfinfo->bitmask) {
            fill_label_bitfield64(fi, label_str, true);
        } else {
            fill_label_number64(fi, label_str, true);
        }
        break;
    case FT_FLOAT:
    case FT_DOUBLE:
        fill_label_float(fi, label_str);
        break;
//        case FT_ABSOLUTE_TIME:
//            tmp = abs_time_to_str(NULL, fvalue_get_time(fi->value), hfinfo->display, true);
//            label_fill(label_str, 0, hfinfo, tmp);
//            wmem_free(NULL, tmp);
//            break;
//        case FT_RELATIVE_TIME:
//            tmp = rel_time_to_secs_str(NULL, fvalue_get_time(fi->value));
//            snprintf(label_str, ITEM_LABEL_LENGTH,
//                   "%s: %s seconds", hfinfo->name, tmp);
//            wmem_free(NULL, tmp);
//            break;
//        case FT_IPXNET:
//            integer = fvalue_get_uinteger(fi->value);
//            tmp = get_ipxnet_name(NULL, integer);
//            snprintf(label_str, ITEM_LABEL_LENGTH,
//                   "%s: %s (0x%08X)", hfinfo->name,
//                   tmp, integer);
//            wmem_free(NULL, tmp);
//            break;
//        case FT_VINES:
//            addr.type = AT_VINES;
//            addr.len  = VINES_ADDR_LEN;
//            addr.data = fvalue_get_bytes_data(fi->value);

//            addr_str = (char*)address_to_str(NULL, &addr);
//            snprintf(label_str, ITEM_LABEL_LENGTH,
//                   "%s: %s", hfinfo->name, addr_str);
//            wmem_free(NULL, addr_str);
//            break;
//        case FT_ETHER:
//            bytes = fvalue_get_bytes_data(fi->value);

//            addr.type = AT_ETHER;
//            addr.len  = 6;
//            addr.data = bytes;

//            addr_str = (char*)address_with_resolution_to_str(NULL, &addr);
//            snprintf(label_str, ITEM_LABEL_LENGTH,
//                   "%s: %s", hfinfo->name, addr_str);
//            wmem_free(NULL, addr_str);
//            break;
//        case FT_IPv4:
//            ipv4 = fvalue_get_ipv4(fi->value);
//            set_address_ipv4(&addr, ipv4);

//            if (hfinfo->display == BASE_NETMASK) {
//                addr_str = (char*)address_to_str(NULL, &addr);
//            } else {
//                addr_str = (char*)address_with_resolution_to_str(NULL, &addr);
//            }
//            snprintf(label_str, ITEM_LABEL_LENGTH,
//                   "%s: %s", hfinfo->name, addr_str);
//            wmem_free(NULL, addr_str);
//            free_address(&addr);
//            break;
//        case FT_IPv6:
//            ipv6 = fvalue_get_ipv6(fi->value);
//            set_address_ipv6(&addr, ipv6);

//            addr_str = (char*)address_with_resolution_to_str(NULL, &addr);
//            snprintf(label_str, ITEM_LABEL_LENGTH,
//                   "%s: %s", hfinfo->name, addr_str);
//            wmem_free(NULL, addr_str);
//            free_address(&addr);
//            break;
//        case FT_FCWWN:
//            bytes = fvalue_get_bytes_data(fi->value);
//            addr.type = AT_FCWWN;
//            addr.len  = FCWWN_ADDR_LEN;
//            addr.data = bytes;

//            addr_str = (char*)address_with_resolution_to_str(NULL, &addr);
//            snprintf(label_str, ITEM_LABEL_LENGTH,
//                   "%s: %s", hfinfo->name, addr_str);
//            wmem_free(NULL, addr_str);
//            break;
//        case FT_GUID:
//            guid = fvalue_get_guid(fi->value);
//            tmp = guid_to_str(NULL, guid);
//            label_fill(label_str, 0, hfinfo, tmp);
//            wmem_free(NULL, tmp);
//            break;
//        case FT_OID:
//            bytes = fvalue_get_bytes_data(fi->value);
//            name = oid_resolved_from_encoded(NULL, bytes, (int)fvalue_length2(fi->value));
//            tmp = oid_encoded2string(NULL, bytes, (unsigned)fvalue_length2(fi->value));
//            if (name) {
//                label_fill_descr(label_str, 0, hfinfo, tmp, name);
//                wmem_free(NULL, name);
//            } else {
//                label_fill(label_str, 0, hfinfo, tmp);
//            }
//            wmem_free(NULL, tmp);
//            break;
//        case FT_REL_OID:
//            bytes = fvalue_get_bytes_data(fi->value);
//            name = rel_oid_resolved_from_encoded(NULL, bytes, (int)fvalue_length2(fi->value));
//            tmp = rel_oid_encoded2string(NULL, bytes, (unsigned)fvalue_length2(fi->value));
//            if (name) {
//                label_fill_descr(label_str, 0, hfinfo, tmp, name);
//                wmem_free(NULL, name);
//            } else {
//                label_fill(label_str, 0, hfinfo, tmp);
//            }
//            wmem_free(NULL, tmp);
//            break;
//        case FT_SYSTEM_ID:
//            bytes = fvalue_get_bytes_data(fi->value);
//            tmp = print_system_id(NULL, bytes, (int)fvalue_length2(fi->value));
//            label_fill(label_str, 0, hfinfo, tmp);
//            wmem_free(NULL, tmp);
//            break;
//        case FT_EUI64:
//            integer64 = fvalue_get_uinteger64(fi->value);
//            addr_str = eui64_to_str(NULL, integer64);
//            tmp = (char*)eui64_to_display(NULL, integer64);
//            label_fill_descr(label_str, 0, hfinfo, tmp, addr_str);
//            wmem_free(NULL, tmp);
//            wmem_free(NULL, addr_str);
//            break;
    case FT_STRING:
    case FT_STRINGZ:
    case FT_UINT_STRING:
    case FT_STRINGZPAD:
    case FT_STRINGZTRUNC:
    case FT_AX25:
        str = fvalue_get_string(fi->value);
        label_fill(label_str, 0, hfinfo, str);
        break;
//        case FT_IEEE_11073_SFLOAT:
//        case FT_IEEE_11073_FLOAT:
//            tmp = fvalue_to_string_repr(NULL, fi->value, FTREPR_DISPLAY, hfinfo->display);
//            snprintf(label_str, ITEM_LABEL_LENGTH,
//                        "%s: %s",
//                        hfinfo->name, tmp);
////            wmem_free(NULL, tmp);
//            break;
    default:
        REPORT_DISSECTOR_BUG("field %s has type %d (%s) not handled in proto_item_fill_label()",
                     hfinfo->abbrev,
                     hfinfo->type,
                     ftype_name(hfinfo->type));
        break;
    }
}
